﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;

namespace CharacterModel
{
    public abstract class DynamicLayerBuilder
    {
        public abstract IEnumerable<Layer> GetDynamicLayers(Feature feature);

        protected static Layer GetLayerSection(Rect boundingBox, Layer headBlack, LayerTypeEnum layerType)
        {
            var polyLineSegment = new PolyLineSegment(new[]
                                                          {
                                                              boundingBox.TopRight,
                                                              boundingBox.BottomRight,
                                                              boundingBox.BottomLeft
                                                          }, false);
            var pathFigure = new PathFigure(boundingBox.TopLeft, new[] {polyLineSegment}, true);
            var geometry = new PathGeometry(new[] {pathFigure});

            var newPathSet = headBlack
                .Paths
                .Select(svgPath => Geometry.Combine(svgPath.Path, geometry, GeometryCombineMode.Intersect, null))
                .Select(g => new LayerSvgPath { SvgPath = g.ToString().Replace("F1", "") })
                .ToList();

            var layer = new Layer
                            {
                                LayerType = new LayerType {LayerTypeEnum = layerType}
                            };
            foreach (var path in newPathSet)
                layer.Paths.Add(path);
            return layer;
        }
    }

    public class MouthLayerBuilder : DynamicLayerBuilder
    {
        public override IEnumerable<Layer> GetDynamicLayers(Feature feature)
        {
            return feature
                .Layers
                .Where(l => new[] {LayerTypeEnum.MouthBlack, LayerTypeEnum.MouthDarkHilight, LayerTypeEnum.MouthShading}
                                .Contains(l.LayerType.LayerTypeEnum));
        }
    }

    public class NoseLayerBuilder : DynamicLayerBuilder
    {
        public override IEnumerable<Layer> GetDynamicLayers(Feature feature)
        {
            return feature
                .Layers
                .Where(l => new[] {LayerTypeEnum.NoseBlack, LayerTypeEnum.NoseDarkHilight, LayerTypeEnum.NoseShading}
                                .Contains(l.LayerType.LayerTypeEnum));
        }
    }

    public class HeadLayerBuilder : DynamicLayerBuilder
    {
        public override IEnumerable<Layer> GetDynamicLayers(Feature feature)
        {
            if (feature.FeatureType.FeatureTypeEnum != FeatureTypeEnum.Head) return new Layer[] {};

            var headBlack = feature.Layers.FirstOrDefault(layer => layer.LayerType.LayerTypeEnum == LayerTypeEnum.HeadBlack);
            var jawTopLeft = feature.MarkerPoints.FirstOrDefault(mp => mp.MarkerPointType.MarkerPointTypeEnum == MarkerPointTypeEnum.JawLineTopLeft);
            var jawTopRight = feature.MarkerPoints.FirstOrDefault(mp => mp.MarkerPointType.MarkerPointTypeEnum == MarkerPointTypeEnum.JawLineTopRight);

            var layers = new List<Layer>();

            if (jawTopLeft == null || jawTopRight == null || headBlack == null) return layers;

            var jawTopY = Math.Max(jawTopLeft.PointY, jawTopRight.PointY);
            var topBoundingBox = new Rect(feature.BoundsLeft, feature.BoundsTop, feature.BoundsWidth,
                                          jawTopY - feature.BoundsTop);
            var bottomBoundingBox = new Rect(feature.BoundsLeft, jawTopY, feature.BoundsWidth,
                                             feature.BoundsHeight - (jawTopY - feature.BoundsTop));

            layers.Add(GetLayerSection(topBoundingBox, headBlack, LayerTypeEnum.DynamicSkullShape));
            layers.Add(GetLayerSection(bottomBoundingBox, headBlack, LayerTypeEnum.DynamicJawLine));

            var headDark = feature.Layers.FirstOrDefault(layer => layer.LayerType.LayerTypeEnum == LayerTypeEnum.HeadDarkHilight);
            if (headDark != null)
            {
                layers.Add(GetLayerSection(topBoundingBox, headDark, LayerTypeEnum.DynamicSkullShapeDark));
                layers.Add(GetLayerSection(bottomBoundingBox, headDark, LayerTypeEnum.DynamicJawDarkHilight));
            }

            var headShading = feature.Layers.FirstOrDefault(layer => layer.LayerType.LayerTypeEnum == LayerTypeEnum.HeadShading);
            if (headShading != null)
            {
                layers.Add(GetLayerSection(topBoundingBox, headShading, LayerTypeEnum.DynamicSkullShapeShading));
                layers.Add(GetLayerSection(bottomBoundingBox, headShading, LayerTypeEnum.DynamicJawShading));
            }
            return layers;
        }
    }

    public class EyeLayerBuilder : DynamicLayerBuilder
    {
        public override IEnumerable<Layer> GetDynamicLayers(Feature feature)
        {
            if (feature.FeatureType.FeatureTypeEnum != FeatureTypeEnum.Eyes) return new Layer[] {};

            var layers = new List<Layer>();
            var eyesBlack = feature.Layers.FirstOrDefault(layer => layer.LayerType.LayerTypeEnum == LayerTypeEnum.EyesBlack);
            var leftPupil = feature.MarkerPoints.FirstOrDefault(mp => mp.MarkerPointType.MarkerPointTypeEnum == MarkerPointTypeEnum.LeftEyeIrisCenter);
            var rightPupil = feature.MarkerPoints.FirstOrDefault(mp => mp.MarkerPointType.MarkerPointTypeEnum == MarkerPointTypeEnum.RightEyeIrisCenter);

            if (eyesBlack == null || leftPupil == null || rightPupil == null) return layers;

            var centerLine = (rightPupil.PointX - leftPupil.PointX) / 2 + leftPupil.PointX;
            var leftBoundingBox = new Rect(feature.BoundsLeft, feature.BoundsTop, centerLine - feature.BoundsLeft, feature.BoundsHeight);
            var rightBoundingBox = new Rect(centerLine, feature.BoundsTop, feature.BoundsWidth - (centerLine - feature.BoundsLeft), feature.BoundsHeight);
            var layerLeft = GetLayerSection(leftBoundingBox, eyesBlack, LayerTypeEnum.DynamicLeftEyeBlack);
            var layerRight = GetLayerSection(rightBoundingBox, eyesBlack, LayerTypeEnum.DynamicRightEyeBlack);

            layers.Add(layerLeft);
            layers.Add(layerRight);

            var eyesDark = feature.Layers.FirstOrDefault(layer => layer.LayerType.LayerTypeEnum == LayerTypeEnum.EyesDarkHilight);
            if (eyesDark != null)
            {
                layers.Add(GetLayerSection(leftBoundingBox, eyesDark, LayerTypeEnum.DynamicLeftEyeDark));
                layers.Add(GetLayerSection(rightBoundingBox, eyesDark, LayerTypeEnum.DynamicRightEyeDark));
            }

            return layers;
        }
    }
}